# coding: utf-8
#
# Copyright (c) Microsoft Corporation.  All rights reserved.
#
##
# Module containing classes to generate sh scripts used by installers
#
# Date:   2007-09-25 14:47:29
#

from shfunction import SHFunction
import scxutil
from cStringIO import StringIO
from scxexceptions import PlatformNotImplementedError

##
# Class for creating a script file.
#
class Script:
    ##
    # Ctor
    # \param[in] platform Platform to generate scripts for.
    #
    def __init__(self, platform):
        self.platform = platform

        # Get the path to 'sh' based on the platform
        self.header = [ scxutil.Get_sh_path(platform.OS) ]

        # Map of all shell global variables.
        self.variableMap = {
            'SCXHOME':             '/opt/microsoft/scx',
            'SCX_TMP_PATH':        '/var/opt/microsoft/scx/tmp',
            'PAM_COMMENT':         '# The configuration of scx is generated by the scx installer.',
            'PAM_COMMENT_CLOSING': '# End of section generated by the scx installer.',
            'PAM_CONF_FILE':       '/etc/pam.conf',
            'PAM_CONF_DIR':        '/etc/pam.d'
            }
        if platform.OS == "SunOS":
            if platform.MinorVersion <= 9:
                self.variableMap['pam_dir_passwd'] = 'auth requisite          pam_authtok_get.so.1\n' + \
                                                     'auth required           pam_dhkeys.so.1\n' + \
                                                     'auth required           pam_unix_auth.so.1\n' + \
                                                     'account requisite       pam_roles.so.1\n' + \
                                                     'account required        pam_unix_account.so.1'
            else:
                self.variableMap['pam_dir_passwd'] = 'auth requisite          pam_authtok_get.so.1\n' + \
                                                     'auth required           pam_dhkeys.so.1\n' + \
                                                     'auth required           pam_unix_cred.so.1\n' + \
                                                     'auth required           pam_unix_auth.so.1\n' + \
                                                     'account requisite       pam_roles.so.1\n' + \
                                                     'account required        pam_unix_account.so.1'
        elif self.platform.OS == "Linux":
            if self.platform.Distribution == 'REDHAT':
                self.variableMap['pam_dir_passwd'] = 'auth       include      system-auth\n' + \
                                                     'account    required     pam_nologin.so\n' + \
                                                     'account    include      system-auth'
            elif self.platform.Distribution == 'SUSE':
                self.variableMap['pam_dir_passwd'] = 'auth     include        common-auth\n' + \
                                                     'auth     required       pam_nologin.so\n' + \
                                                     'account  include        common-account'
            elif self.platform.Distribution == 'UBUNTU':
                self.variableMap['pam_dir_passwd'] = 'auth     required       pam_env.so\n' + \
                                                     'auth     required       pam_unix.so nullok_secure\n' + \
                                                     'account  required       pam_unix.so\n' + \
                                                     'session  required       pam_limits.so'
            else:
                raise PlatformNotImplementedError(self.platform.Distribution)
        elif self.platform.OS == "HPUX":
            self.variableMap['pam_dir_passwd'] = 'auth required  libpam_hpsec.so.1\n' + \
                                                 'auth required  libpam_unix.so.1\n' + \
                                                 'account required       libpam_hpsec.so.1\n' + \
                                                 'account required       libpam_unix.so.1'
        elif self.platform.OS == "AIX":
            self.variableMap['pam_dir_passwd'] = 'auth    required        /usr/lib/security/pam_aix\n' + \
                                                 'account required        /usr/lib/security/pam_aix'
        elif self.platform.OS == "MacOS":
            self.variableMap['pam_dir_passwd'] = 'auth       required       pam_nologin.so\n' + \
                                                 'auth       sufficient     pam_securityserver.so\n' + \
                                                 'auth       sufficient     pam_unix.so\n' + \
                                                 'auth       required       pam_deny.so\n' + \
                                                 'account    required       pam_securityserver.so'
        else:
            raise PlatformNotImplementedError(self.platform.OS)
        
        self.variableMap['pam_file_passwd'] = 'scx    ' + self.variableMap['pam_dir_passwd'].replace('\n', '\nscx    ')


        if platform.OS == "MacOS":
            self.variableMap['SCXHOME'] = '/usr/libexec/microsoft/scx'

        self.functionList = []
        self.mainSection = ['#',
                            '# Main section',
                            '#']

    ##
    # Write line to main section of script.
    # \param[in] line Line to add to main section.
    #
    def WriteLn(self, line):
        self.mainSection = self.mainSection + [
            line
            ]
        
    ##
    # Inserts a call to a function in the main section.
    # The function definition is added to the functions section
    #
    def CallFunction(self, function, arguments = ''):
        self.mainSection = self.mainSection + [
            function.Name() + ' ' + arguments
            ]
        self.DefineFunction(function)

    ##
    # The function definition is added to the functions section
    # and a call to this function is returned.
    #
    def CallSubFunction(self, function, arguments = ''):
        self.DefineFunction(function)
        return function.Name() + ' ' + arguments

    ##
    # Inserts a function definition in the main section.
    # The function is not explicitly called.
    #
    def DefineFunction(self, function):
        for definedfunction in self.functionList:
            if function.Name() == definedfunction.Name():
                return
        self.functionList.append(function)

    ##
    # Writes the script to the output.
    #
    def WriteTo(self, output):
        # Header information
        for line in self.header:
            output.write(line + '\n')
        output.write('\n')

        # Functions
        for function in self.functionList:
            output.write(function.Definition(self.variableMap))
            output.write('\n')
        output.write('\n')

        # Main section
        for line in self.mainSection:
            output.write(line + '\n')

    ##
    # Writes the installinfo file containing creation date and version string.
    #
    def WriteInstallInfo(self, version, buildNr):
        return SHFunction('write_install_info',
                          ['date +%%Y-%%m-%%dT%%T.0Z > %(INSTALL_INFO_FILE)s',
                           'echo %(VERSION_STRING)s >> %(INSTALL_INFO_FILE)s',
                           'return 0'
                           ],
                          {'INSTALL_INFO_FILE': '/etc/opt/microsoft/scx/conf/installinfo.txt',
                           'VERSION_STRING': version + '-' + buildNr})

    ##
    # Generates the ssl certificate.
    #
    def GenerateCertificate(self):
        varmap = {'SSL_DIR':           '/etc/opt/microsoft/scx/ssl',
                  'GENCERT_CERT_FILE': 'scx-host-`uname -n`.pem',
                  'SCX_SSL_CONFIG':    '/opt/microsoft/scx/bin/tools/scxsslconfig'}

        if self.platform.OS == "MacOS":
            varmap['SCX_SSL_CONFIG'] = '/usr/libexec/microsoft/scx/bin/tools/scxsslconfig'

        body = [
            'if [ ! \"%(SSL_DIR)s\" = \"\" ] && [ -d %(SSL_DIR)s ]; then'
            ]

        # TODO: Shouldn't this test for solaris or something?
        if self.platform.MajorVersion == 5 and self.platform.MinorVersion <= 9:
            body = body + [
                ' LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/ssl/lib',
                ' export LD_LIBRARY_PATH'
                ]

        body = body + [
            '  if [ -f %(SSL_DIR)s/scx-seclevel1-key.pem ] && [ ! -f %(SSL_DIR)s/scx-key.pem ]; then',
            '    mv -f %(SSL_DIR)s/scx-seclevel1-key.pem %(SSL_DIR)s/scx-key.pem',
            '  fi',
            '  if [ -f %(SSL_DIR)s/scx-seclevel1.pem ] && [ ! -f %(SSL_DIR)s/scx.pem ]; then',
            '    mv -f %(SSL_DIR)s/scx-seclevel1.pem %(SSL_DIR)s/scx-host-`hostname`.pem',
            '    ln -s -f %(SSL_DIR)s/scx-host-`hostname`.pem %(SSL_DIR)s/scx.pem',
            '  fi',
            '  %(SCX_SSL_CONFIG)s',
            '  if [ $? -ne 0 ]; then',
            '    exit 1', # No need to print reason since SSL tool does that.
            '  fi',
            'else',
            '  # %(SSL_DIR)s : directory does not exist',
            '  exit 1',
            'fi',
            'return 0'
            ]
        
        return SHFunction('generate_certificate', body, varmap)

    ##
    # Decides on what pam configuration should be used
    # based on sshd configuration found in pam.conf file
    #
    def GetNewPAMConfig_file(self):
        body = [
            '#',
            '# Get configuration for sshd, service modules types auth and account',
            '#',
            'sshd_conf=`egrep \"^[# ]*sshd[ ]+(auth|account)\" %(PAM_CONF_FILE)s`',
            'if [ $? -ne 0 ]; then',
            '  # No match found',
            '  # sshd not explicitly configured.',
            '  # Use passwd',
            '  sshd_conf=`echo \"%(pam_file_passwd)s\"`',
            'fi',
            '',
            '#',
            '# Substitute sshd with scx.',
            '#',
            'scx_conf=`echo \"$sshd_conf\" | sed \"s/sshd/scx/g\"`',
            'if [ $? -ne 0 ]; then',
            '  echo \"can\'t parse %(PAM_CONF_FILE)s\"',
            '  return 1',
            'fi'
            ]
        return SHFunction('get_new_pam_config_file', body)
        
    ##
    # Decides on what pam configuration should be used
    # based on sshd configuration found in pam.d directory
    #
    def GetNewPAMConfig_dir(self):
        body = [
            '#',
            '# Get configuration for sshd, service modules types auth and account',
            '#',
            'sshd_conf=`egrep \"(auth|account)\" %(PAM_CONF_DIR)s/sshd 2> /dev/null`',
            'if [ $? -ne 0 ]; then',
            '  # No match found',
            '  # sshd not explicitly configured.',
            '  # Use passwd',
            '  sshd_conf=`echo \"%(pam_dir_passwd)s\"`',
            'fi',
            '',
            'scx_conf=$sshd_conf'
            ]
        return SHFunction('get_new_pam_config_dir', body)
        
    ##
    # Configure PAM in /etc/pam.conf
    #
    def ConfigurePAM_file(self):
        body = [
            '#',
            '# First check if scx is already configured in pam.conf',
            '#',
            'grep -s \"^[# ]*scx\" %(PAM_CONF_FILE)s > /dev/null 2>&1',
            'if [ $? -eq 0 ]; then',
            '  # Match found',
            '  # Looks like scx is already configured',
            '  echo \"scx already configured\"',
            '  return 0',
            'fi',
            '',
            self.CallSubFunction(self.GetNewPAMConfig_file()),
            '',
            '#',
            '# Write the final configuration to pam.conf',
            '#',
            '# copy file first and modify this copy, so in case of low disk space we preserve the original file',
            'cp %(PAM_CONF_FILE)s %(PAM_CONF_FILE)s.scx-copy && echo \"%(PAM_COMMENT)s\n$scx_conf\n%(PAM_COMMENT_CLOSING)s\n\" >> %(PAM_CONF_FILE)s.scx-copy',
            'if [ $? -ne 0 ]; then',
            '  echo \"can\'t update file %(PAM_CONF_FILE)s.scx-copy\"',
            '  rm -f %(PAM_CONF_FILE)s.scx-copy',
            '  return 1',
            'fi',
            '# verify that complete file was written ',
            'grep "%(PAM_COMMENT_CLOSING)s" %(PAM_CONF_FILE)s.scx-copy > /dev/null 2>&1',
            'if [ $? -ne 0 ]; then',
            '  echo \"can\'t update file %(PAM_CONF_FILE)s.scx-copy\"',
            '  rm -f %(PAM_CONF_FILE)s.scx-copy',
            '  return 1',
            'fi',
            '# use move to substitute original file with verified copy',
            'mv %(PAM_CONF_FILE)s.scx-copy %(PAM_CONF_FILE)s',
            'if [ $? -ne 0 ]; then',
            '  echo \"can\'t replace %(PAM_CONF_FILE)s\"',
            '  return 1',
            'fi'
            ]
        return SHFunction('configure_pam_file', body)

    ##
    # Configure PAM in /etc/pam.d
    #
    def ConfigurePAM_dir(self):
        body = [
            '#',
            '# First check if scx is already configured',
            '#',
            'if [ -f %(PAM_CONF_DIR)s/scx ]; then',
            '  # Match found',
            '  # Looks like scx is already configured',
            '  echo \"scx already configured\"',
            '  return 0',
            'fi',
            '',
            self.CallSubFunction(self.GetNewPAMConfig_dir()),
            'echo \"#%%PAM-1.0\n%(PAM_COMMENT)s\n$scx_conf\" > %(PAM_CONF_DIR)s/scx',
            'if [ $? -ne 0 ]; then',
            '  echo \"can\'t create %(PAM_CONF_DIR)s/scx\"',
            '  return 1',
            'fi'
            ]
        return SHFunction('configure_pam_dir', body)

    ##
    # Configures PAM (Either /etc/pam.conf or /etc/pam.d)
    #
    def ConfigurePAM(self):
        body = ['#',
                '# Check if pam is configured with single',
                '# configuration file or with configuration',
                '# directory.',
                '#',
                'if [ -s %(PAM_CONF_FILE)s ]; then',
                '  ' + self.CallSubFunction(self.ConfigurePAM_file()),
                'elif [ -d %(PAM_CONF_DIR)s ]; then',
                '  ' + self.CallSubFunction(self.ConfigurePAM_dir()),
                'else',
                '  # No pam configuration.',
                '  echo \"PAM does not seem to be configured.\"',
                '  echo \"Checked both %(PAM_CONF_FILE)s and %(PAM_CONF_DIR)s.\"',
                '  return 1',
                'fi',
                'return 0'
                ]
        return SHFunction('configure_pam', body)

    ##
    # Determines the current SCX PAM configuration
    # from a pam.conf file.
    #
    def GetCurrentPAMConfig_file(self):
        body = [
            'scx_current_conf=`grep \"^[#\t]*scx\" %(PAM_CONF_FILE)s`',
            ]
        return SHFunction('get_current_pam_config_file', body)

    ##
    # Determines the current SCX PAM configuration
    # from a pam.conf file.
    #
    def GetCurrentPAMConfig_dir(self):
        body = [
            'scx_current_conf=`cat %(PAM_CONF_DIR)s/scx | grep -v \"#%%PAM-1.0\" | grep -v \"%(PAM_COMMENT)s\"`',
            ]
        return SHFunction('get_current_pam_config_dir', body)

    ##
    # Remove scx configuration from a pam.conf file
    #
    def UnconfigurePAM_file(self):
        body = [
            '# Configured with single file',
            '']
        
        # For SunOS we want to be a little smarter than just removing the file
        # since on uninstall for solaris we don't know if it is part of an
        # upgrade. So we only want to remove the configuration if a clean
        # installation would result in the same configuration as we have now.
        if self.platform.OS == "SunOS":
            body.extend([
                self.CallSubFunction(self.GetNewPAMConfig_file()), # The result will be available in the $scx_conf variable
                self.CallSubFunction(self.GetCurrentPAMConfig_file()), # The result will be available in the $scx_current_conf variable
                'if [ \"$scx_conf\" != \"$scx_current_conf\" ]; then',
                '  # It looks like the configuration has been changed since',
                '  # installation or at least would change if we did a reinstall',
                '  # Safest to leave the configuration as is.',
                '  return 0',
                'fi'
                ])
        body.extend([
            '#',
            '# Get all lines except scx configuration',
            '#',
            'pam_configuration=`grep -v \"^[#\t]*scx\" %(PAM_CONF_FILE)s | grep -v \"%(PAM_COMMENT)s\" | grep -v \"%(PAM_COMMENT_CLOSING)s\"`',
            'if [ $? -ne 0 ]; then',
            '  # scx not configured in PAM',
            '  return 0',
            'fi',
            '#',
            '# Write it back (to the copy first)',
            '#',
            'cp -p %(PAM_CONF_FILE)s %(PAM_CONF_FILE)s.tmp',
            'echo \"$pam_configuration\" > %(PAM_CONF_FILE)s.tmp',
            'if [ $? -ne 0 ]; then',
            '  echo \"can\'t write to %(PAM_CONF_FILE)s.tmp\"',
            '  return 1',
            'fi',
            'mv %(PAM_CONF_FILE)s.tmp %(PAM_CONF_FILE)s',
            'if [ $? -ne 0 ]; then',
            '  echo \"can\'t replace %(PAM_CONF_FILE)s\"',
            '  return 1',
            'fi'
            ])
        return SHFunction('unconfigure_pam_file', body)

    ##
    # Remove scx configuration from a pam.d directory
    #
    def UnconfigurePAM_dir(self):
        body = [
            '# Configured with directory',
            'if [ -f %(PAM_CONF_DIR)s/scx ]; then'
            ]

        # For SunOS we want to be a little smarter than just removing the file
        # since on uninstall for solaris we don't know if it is part of an
        # upgrade. So we only want to remove the configuration if a clean
        # installation would result in the same configuration as we have now.
        if self.platform.OS == "SunOS":
            body.extend([
                self.CallSubFunction(self.GetNewPAMConfig_dir()), # The result will be available in the $scx_conf variable
                self.CallSubFunction(self.GetCurrentPAMConfig_dir()), # The result will be available in the $scx_current_conf variable
                'if [ \"$scx_conf\" != \"$scx_current_conf\" ]; then',
                '  # It looks like the configuration has been changed since',
                '  # installation or at least would change if we did a reinstall',
                '  # Safest to leave the configuration as is.',
                '  return 0',
                'fi'
                ])

        body.extend([
            '  rm -f %(PAM_CONF_DIR)s/scx',
            '  return 0',
            'fi'
            ])
        return SHFunction('unconfigure_pam_dir', body)

    ##
    # Removes scx configuration from PAM
    #
    def UnconfigurePAM(self):
        body = [
            '#',
            '# Check if pam is configured with single',
            '# configuration file or with configuration',
            '# directory.',
            '#',
            'if [ -s %(PAM_CONF_FILE)s ]; then',
            '  ' + self.CallSubFunction(self.UnconfigurePAM_file()),
            'elif [ -d %(PAM_CONF_DIR)s ]; then',
            '  ' + self.CallSubFunction(self.UnconfigurePAM_dir()),
            'fi',
            'return 0'
            ]
        return SHFunction('unconfigure_pam', body)

    ##
    # Installs scx-cimd as a service using native service management.
    #
    def ConfigurePegasusService(self):
        if self.platform.OS == "SunOS":
            if self.platform.MajorVersion == 5 and self.platform.MinorVersion < 10:
                body = ['return 0']
            else:
                body = ['svccfg import /var/svc/manifest/application/management/scx-cimd.xml']
        elif self.platform.OS == "Linux":
            if self.platform.Distribution == 'UBUNTU':
                body = ['update-rc.d scx-cimd defaults']
            else:
                body = ['/usr/lib/lsb/install_initd /etc/init.d/scx-cimd']
        elif self.platform.OS == "HPUX":
            body = ['return 0']
        elif self.platform.OS == "AIX":
            body = ['mkssys -s scx-cimd' + \
                    ' -p %(SCXHOME)s/bin/scxcimserver' + \
                    ' -u root -S -n 15 -f 9 -G scx',
                    'grep scx-cimd /etc/inittab > /dev/null 2>&1',
                    'if [ $? -eq 0 ]; then',
                    'rmitab scx-cimd',
                    'fi',
                    'mkitab "scx-cimd:2:once:/usr/bin/startsrc -s scx-cimd > /dev/console 2>&1"']
        elif self.platform.OS == "MacOS":
            body = ['return 0']

        return SHFunction('configure_pegasus_service', body)

    ##
    # Starts the scx-cimd service
    #
    def StartPegasusService(self):
        if self.platform.OS == "SunOS":
            if self.platform.MajorVersion == 5 and self.platform.MinorVersion < 10:
                body = ['/etc/init.d/scx-cimd start']
            else:
                body = ['svcadm enable svc:/application/management/scx-cimd']
        elif self.platform.OS == "Linux":
            if self.platform.Distribution == "SUSE" and self.platform.MajorVersion == 9:
                body = ['/etc/init.d/scx-cimd start']
            elif self.platform.Distribution == "UBUNTU":
                body = ['invoke-rc.d scx-cimd start']
            else:
                body = ['service scx-cimd start']
        elif self.platform.OS == "HPUX":
            body = ['/sbin/init.d/scx-cimd start']
        elif self.platform.OS == "AIX":
            body = ['startsrc -s scx-cimd']
        elif self.platform.OS == "MacOS":
            body = ['launchctl load -w /Library/LaunchDaemons/com.microsoft.scx-cimd.plist']
        else:
            raise PlatformNotImplementedError(self.platform.OS)
        
        return SHFunction('start_pegasus_service', body)

    ##
    # Stops the scx-cimd service
    #
    def StopPegasusService(self):
        if self.platform.OS == "SunOS":
            if self.platform.MajorVersion == 5 and self.platform.MinorVersion < 10:
                body = ['/etc/init.d/scx-cimd stop']
            else:
                body = ['svcadm disable -s svc:/application/management/scx-cimd']
        elif self.platform.OS == "Linux":
            if self.platform.Distribution == "SUSE" and self.platform.MajorVersion == 9:
                body = ['/etc/init.d/scx-cimd stop']
            elif self.platform.Distribution == "UBUNTU":
                body = ['invoke-rc.d scx-cimd stop']
            else:
                body = ['service scx-cimd stop']
        elif self.platform.OS == "HPUX":
            body = ['/sbin/init.d/scx-cimd stop']
        elif self.platform.OS == "AIX":
            body = ['stopsrc -c -s scx-cimd']
        elif self.platform.OS == "MacOS":
            body = ['launchctl unload -w /Library/LaunchDaemons/com.microsoft.scx-cimd.plist']
        else:
            raise PlatformNotImplementedError(self.platform.OS)

        return SHFunction('stop_pegasus_service', body)

    ##
    # Stops the scx-wsmand service
    #
    def StopWSManService(self):
        if self.platform.OS == "Linux":
            if self.platform.MajorVersion == 9 and self.platform.Distribution == "SUSE":
                body = [
                    'if [ -f /etc/init.d/scx-wsmand ]; then',
                    '  /etc/init.d/scx-wsmand stop',
                    'fi'
                    ]
            else:
                body = [
                    'if [ -f /etc/init.d/scx-wsmand ]; then',
                    '  service scx-wsmand stop',
                    'fi'
                    ]
        elif self.platform.OS == "HPUX":
            body = [
                'if [ -f /sbin/init.d/scx-wsmand ]; then',
                '  /sbin/init.d/scx-wsmand stop',
                'fi'
                ]
        elif self.platform.OS == "AIX":
            body = [
                'if [ -f %(SCXHOME)s/bin/scxopenwsmand ]; then',
                '  stopsrc -c -s scx-wsmand',
                'fi'
                ]
        elif self.platform.OS == "MacOS":
            body = ['launchctl unload -w /Library/LaunchDaemons/com.microsoft.scx-wsmand.plist']
        else:
            raise PlatformNotImplementedError(self.platform.OS)

        return SHFunction('stop_wsman_service', body)

    ##
    # Unregisters the scx-cimd service
    #
    def RemovePegasusService(self):
        if self.platform.OS == "SunOS":
            if self.platform.MajorVersion == 5 and self.platform.MinorVersion < 10:
                body = ['return 0']
            else:
                body = ['svccfg delete svc:/application/management/scx-cimd:default']
        elif self.platform.OS == "Linux":
            if self.platform.Distribution == 'UBUNTU':
                body = ['update-rc.d -f scx-cimd remove']
            else:
                body = ['/usr/lib/lsb/remove_initd /etc/init.d/scx-cimd']
        elif self.platform.OS == "AIX":
            body = ['rmssys -s scx-cimd',
                    'rmitab scx-cimd']
        elif self.platform.OS == "HPUX":
            body = ['return 0']
        elif self.platform.OS == "MacOS":
            body = ['return 0']
        else:
            raise PlatformNotImplementedError(self.platform.OS)

        return SHFunction('unregister_pegasus_service', body)

    ##
    # Unregisters the scx-wsmand service
    #
    def RemoveWSManService(self):
        if self.platform.OS == "Linux":
            body = [
                'if [ -f /etc/init.d/scx-wsmand ]; then',
                '  /usr/lib/lsb/remove_initd /etc/init.d/scx-wsmand',
                'fi'
                ]
        elif self.platform.OS == "AIX":
            body = [
                'if [ -f %(SCXHOME)s/bin/scxopenwsmand ]; then',
                '  rmssys -s scx-wsmand',
                'fi'
                ]
        elif self.platform.OS == "HPUX":
            body = ['return 0']
        elif self.platform.OS == "MacOS":
            body = ['return 0']
        else:
            raise PlatformNotImplementedError(self.platform.OS)

        return SHFunction('unregister_wsman_service', body)

    ##
    # If the platform does not support upgrade call this method to
    # remove all files that has been removed in this release
    #
    def RemoveDeletedFiles(self):
        body = [
            'rm -f %(SCXHOME)s/lib/libpegCLIClientLib.*',
            'rm -f %(SCXHOME)s/lib/libpegcql.*',
            'rm -f %(SCXHOME)s/bin/scxopenwsmand',
            'rm -f %(SCXHOME)s/lib/libwsman_server.*',
            'rm -f %(SCXHOME)s/lib/libwsman.*',
            'rm -Rf %(SCXHOME)s/lib/openwsman',
            'rm -Rf /var/opt/microsoft/scx/lib/repository/root#scx/classes'
            ]
        if self.platform.OS == "HPUX":
            body.extend([
                'rm -f /sbin/init.d/scx-wsmand',
                'rm -f /sbin/rc2.d/S999scx-wsmand',
                'rm -f /sbin/rc1.d/K100scx-wsmand'
                ])
        else:
            raise PlatformNotImplementedError(self.platform.OS)

        return SHFunction('remove_deleted_files', body)

    ##
    # Configures runas using sshd_config as template
    #
    def ConfigureRunas(self):
        varmap = {}
        varmap['RUNAS_CFG'] = '/etc/opt/microsoft/scx/conf/scxrunas.conf'

        body = [
            'if [ -s %(RUNAS_CFG)s ]; then',
            '  # File is not zero size',
            '  return 0',
            'fi',
            '/opt/microsoft/scx/bin/tools/scxadmin -config-reset RunAs AllowRoot > /dev/null 2>&1'
            ] 
            
        return SHFunction('configure_runas', body, varmap)

    ##
    # Helper shell function that verifies that a required patch is installed on SunOS.
    # It is successful if the the required patch is found, if a newer version of the 
    # same patch is found, or if a patch is found that obsoletes the required patch 
    # (or a newer version of it).
    #
    def VerifyPatchInstalled(self):
        body = [
           'patchadd -p | nawk \'BEGIN { code = 0 }',
           '        { split($2, a, "-"); if (a[1] == base && a[2] >= ver) code=1 }',
           '        { for (i = 4 ; i <= NF; ++i) { if ($i == "Requires:") break;',
           '                                       split($i, a, "-");',
           '                                       if (a[1] == base && a[2] >= ver) code=2 }',
           '                                    }',
           '        END { exit(code) }\' base=$1 ver=$2',
           'if [ $? -eq 0 ]; then',
           '  echo "Required patch $1-$2 was not found. Please install patch and try again.";',
           '  exit 1',
           'fi',
           'return 0'
           ]
        return SHFunction('verify_patch_installed', body)


    ##
    # Checks for additional dependencies that package format can not handle natively
    #
    def CheckAdditionalDeps(self):
        if self.platform.OS == "SunOS" and self.platform.Architecture == "sparc":
            body = [
                'if [ `uname -p` != \"sparc\" ]; then',
                '  echo \"This package only supports sparc architecture.\"',
                '  exit 1',
                'fi'
                ]
            if self.platform.MajorVersion == 5 and self.platform.MinorVersion == 8:
                # Strictly speaking, we should have 109147, 108434, and 108435.  
                # But 109147 is a prereq for 108434 - the patch checks for that, 
                # and 108435 is for 64-bit; and we build 32-bit.
                body = body + [
                    self.CallSubFunction(self.VerifyPatchInstalled(), '108434 22'),
                    'return 0'
                    ]
            elif self.platform.MajorVersion == 5 and self.platform.MinorVersion == 9:
                # We need 112960-48 for bug ID 4974005 (PAM memory leak)
                body = body + [
                    self.CallSubFunction(self.VerifyPatchInstalled(), '112960 48'),
                    'return 0'
                    ]
            elif self.platform.MajorVersion == 5 and self.platform.MinorVersion == 10:
                body = body + [
                    self.CallSubFunction(self.VerifyPatchInstalled(), '117463 05'),
                    'return 0'
                    ]
        elif self.platform.OS == "SunOS" and self.platform.Architecture == "x86":
            body = [
                'if [ `uname -p` != \"i386\" ]; then',
                '  echo \"This package only supports x86 architecture.\"',
                '  exit 1',
                'fi',
                self.CallSubFunction(self.VerifyPatchInstalled(), '117464 04'),
                'return 0'
                ]
        else:
            body = ['return 0']

        return SHFunction('check_additional_deps', body)

    ##
    # Removes files that are left by the package format
    #
    def RemoveAdditionalFiles(self):
        return SHFunction('remove_additional_files',
                          ['rm -r %(SCX_TMP_PATH)s > /dev/null 2>&1',
                           'return 0'])

    ##
    # Removes a directory recursivly but leaves any non-empty directories (i.e. with files).
    #
    def RemoveEmptyDirectoryRecursive(self):
        return SHFunction('remove_empty_directory_recursive',
                          ['find $1 -type d | sort -r |',
                           'while read D',
                           'do',
                           '  ls -l $D | grep -q \'total 0\' && rmdir  $D 2>/dev/null',
                           'done'])

    ##
    # Creates the /usr/sbin/scxadmin link manually (used primarily on Solaris 5.10 and greater)
    #
    def CreateLink_usr_sbin_scxadmin(self):
	if self.platform.OS == 'MacOS':
            return SHFunction('CreateLink_usr_sbin_scxadmin',
                              ['ln -s /usr/libexec/microsoft/scx/bin/tools/scxadmin /usr/sbin/scxadmin > /dev/null 2>&1'])
        else:
            return SHFunction('CreateLink_usr_sbin_scxadmin',
                              ['ln -s /opt/microsoft/scx/bin/tools/scxadmin /usr/sbin/scxadmin > /dev/null 2>&1'])

    ##
    # Removes the /usr/sbin/scxadmin link manually (used primarily on Solaris 5.10 and greater)
    #
    def RemoveLink_usr_sbin_scxadmin(self):
        return SHFunction('RemoveLink_usr_sbin_scxadmin',
                          ['rm /usr/sbin/scxadmin > /dev/null 2>&1'])

    ##
    # Runs cimmof on all .mof files in /opt/microsoft/scx/lib/providers/ext
    #
    def RegisterExtProviders(self):
        varmap = {'cimmof':       self.variableMap['SCXHOME'] + '/bin/tools/scxcimmof',
                  'mofdirectory': self.variableMap['SCXHOME'] + '/lib/providers/ext'}

        body = ['sleep 1',
                'for F in `/bin/ls %(mofdirectory)s/*.mof 2> /dev/null`; do',
                '  %(cimmof)s -n \"root/PG_InterOp\" \"$F\"',
                'done']
            
        return SHFunction('register_ext_providers', body, varmap)

    ##
    # Backs up configuration file (argument 1) to another name so it will not be
    # overwritten by the installation.
    #
    def BackupConfigurationFile(self):
        if self.platform.OS == "HPUX" or self.platform.OS == 'MacOS':
            return SHFunction('backup_configuration_file', ['mv \"$1\" \"$1.swsave\" > /dev/null 2>&1'])
        else:
            raise PlatformNotImplementedError(self.platform.OS)

    ##
    # Restores configuration file (argument 1) from backed up version
    #
    def RestoreConfigurationFile(self):
        if self.platform.OS == "HPUX" or self.platform.OS == 'MacOS':
            return SHFunction('restore_configuration_file', ['mv \"$1.swsave\" \"$1\"'])
        else:
            raise PlatformNotImplementedError(self.platform.OS)
    
        
    ##
    # Installs a configuration file.
    # Does not install the file if it is there already.
    # On Solaris the file is copied to a special location for "safekeeping" as well.
    #
    def InstallConfigurationFile(self):
        if self.platform.OS == "SunOS":
            return SHFunction(
                'install_configuration_file',
                [
                '# Skip /dev/null',
                '[ \"$1\" = /dev/null ] && return 0',
                '',
                '# savepath is where original configuration files are stored',
                '# for later comparison',
                'savepath=$PKGSAV/config$2',
                '# Find the directory part of the savepath',
                'dirname=`dirname $savepath` || return 1',
                '',
                '# If directory does not exist, create it.',
                'if [ ! -d $dirname ]; then',
                '  mkdir -p $dirname',
                'fi'
                '',
                '# If there is an old saved version of the configuration file then use it',
                'if [ -f \"$2.pkgsave\" ]; then',
                '  mv \"$2.pkgsave\" \"$2\" || return 1',
                '  cp $1 $savepath || return 1',
                'else',
                '  echo $2',
                '  cp $1 $2 || return 1',
                '  cp $1 $savepath || return 1',
                'fi',
                'return 0'
                ])
        else:
            raise PlatformNotImplementedError(self.platform.OS)
        
    ##
    # Uninstalls a configuration file.
    # If the file has changes since intall time it is instead renamed
    # as *.pkgsave.
    #
    def UninstallConfigurationFile(self):
        if self.platform.OS == "SunOS":
            return SHFunction(
                'uninstall_configuration_file',
                [
                '# savepath is where original configuration files are stored',
                '# for comparison',
                'savepath=$PKGSAV/config$1',
                '',
                '/usr/bin/diff $1 $savepath > /dev/null',
                'if [ $? -ne 0 ]; then',
                '  # The configuration file has changed since install time.',
                '  mv $1 $1.pkgsave',
                'else',
                '  echo $1',
                '  rm -f $1 || return 1',
                'fi',
                'rm -f $savepath',
                'return 0'
                ])
        else:
            raise PlatformNotImplementedError(self.platform.OS)
        
